﻿namespace Tentacle.Downloader
{
    //
    using System;
    using System.Net;
    using System.Net.Http;
    using System.Net.Http.Headers;
    using System.Net.Sockets;
    using System.Threading;
    using System.Net.Mime;
    using System.Threading.Tasks;
    //
    using Tentacle.Core;
    //
    using static Tentacle.Utils.Hepler;
    using static Tentacle.Utils.TentacleProtocol;
    //
    using TaskRunningState = System.Boolean;

    #region HttpClientHandler

    public sealed class RemotedHttpClientHandler : HttpClientHandler
    {
        protected override Task<HttpResponseMessage> SendAsync(HttpRequestMessage request, CancellationToken cancellationToken)
        {
            //控制UserAgent
            var userAgent = request.Headers.UserAgent;
            userAgent.Clear();

            userAgent.Add(new ProductInfoHeaderValue("Mozilla", "5.0"));
            userAgent.Add(new ProductInfoHeaderValue("AppleWebKit", "537.36"));
            userAgent.Add(new ProductInfoHeaderValue("Chrome", "62.0.3202.94"));
            userAgent.Add(new ProductInfoHeaderValue("Safari", "537.36"));

            //控制Referer
            request.Headers.Referrer = request.RequestUri;

            // 控制Accept
            var accept = request.Headers.Accept;
            accept.Clear();
            accept.Add(new MediaTypeWithQualityHeaderValue(MediaTypeNames.Text.Plain));

            return base.SendAsync(request, cancellationToken);
        }
    }


    #endregion

    #region RemoteWork

    /// <summary>
    /// 默认启动四线程
    /// </summary>
    public class RemoteWork : IDisposable
    {
        

        #region Work

        IServerService     _node1;
        IServerService     _node2;
        IServerService     _node3;
        IServerService     _node4;

        #endregion

        RemotedHttpClientHandler    _handler;
        TaskRunningState   _runningState;
        HttpClient         _client;
        WebToken           _token;
        WebSite            _site;

        #region Constructor

        public RemoteWork()
        {
            _handler = new RemotedHttpClientHandler();
            _client = new HttpClient(_handler);
            _broadcast = new UdpClient(9616);

            InitializeServerService(ref _node1);
            InitializeServerService(ref _node2);
            InitializeServerService(ref _node3);
            InitializeServerService(ref _node4);

            _runningState = true;
            _broadcastService = Task.Factory.StartNew(BroadcastMainLoop);

            _client.Timeout = new TimeSpan(0,0,20);
        }

        void InitializeServerService(ref IServerService service)
        {
            service = new ServerService();
            service.SetSite += OnSetSite;
            service.SetToken += OnSetToken;
            service.Disconnect += Disconnect;

            service.Start();
        }

        #endregion

        #region IServerService Event

        void Disconnect()
        {

        }

        void OnSetToken(string token)
        {
            SetToken?.Invoke();
            _token = WebToken.Parse(token);
        }

        async Task<string> OnSetSite(string site)
        {
            Trace(site);

            _site = new WebSite(site);
            SetSite?.Invoke(site);

            try {
                var response = await _client.GetAsync(site);
                if (response.IsSuccessStatusCode) {
                    Trace("请求成功,正在返回数据");
                    return await response.Content
                                         .ReadAsStringAsync();
                }
                else {
                    Trace("请求失败");
                    return Response_ERROR + ',' + ((int)response.StatusCode).ToString();
                }
            }catch(Exception ex) {                
                Trace(ex);
                return Response_ERROR + ',' + ex.Message;
            }
        }

        #endregion

        #region Broadcast Service


        //---------------------------------------------------------------------------------------------------
        //  当 当前节点 尚未接受连接请求时回应FIND命令
        //---------------------------------------------------------------------------------------------------

        UdpClient   _broadcast;
        Task        _broadcastService;

        async void BroadcastMainLoop()
        {
            while (_runningState) {
                await Task.Delay(1000);
                if (!_node1.Connected) {
                    var buffer = await _broadcast.ReceiveAsync();
                    var msg = GetString(buffer.Buffer);
                    switch (msg) {
                        case Find:
                            await FIND(buffer.RemoteEndPoint);
                            break;
                    }
                }
            }
        }

        #endregion        

        #region TENTACLE Protocol


        //---------------------------------------------------------------------------------------------------
        //  触手协议规定了远程节点之间的通讯方式
        //  命令:
        //
        //  FIND:
        //  广播请求命令，收到该命令之后的所有未与主机建立可靠连接的远端节点需要返回当前的IP地址
        //
        //
        //  SET-TOKEN:
        //  指示将会更新TOKEN
        //  响应:SUCCESS
        //  
        //  SET-SITE:
        //  指示将会设置当前的下载站点
        //  响应为站点数据:WEBPAGE
        //
        //  DISCONNECT:
        //  断开客户端连接
        //  响应:
        //  1.SUCCESS
        //  2.ERROR
        //
        //---------------------------------------------------------------------------------------------------

        async Task FIND(IPEndPoint iP)
        {
            Send(_node1.LocalEndPoint.ToString(), iP, _broadcast);
            await Task.Delay(30);

            Send(_node2.LocalEndPoint.ToString(), iP, _broadcast);
            await Task.Delay(30);

            Send(_node3.LocalEndPoint.ToString(), iP, _broadcast);
            await Task.Delay(30);

            Send(_node4.LocalEndPoint.ToString(), iP, _broadcast);
            await Task.Delay(30);
        }

        public void Dispose()
        {
            _runningState = false;
            _broadcast.Dispose();
            _node1.Dispose();
            _node2.Dispose();
            _node3.Dispose();
            _node4.Dispose();
        }
        #endregion

        #region Override

        public IPEndPoint[] LocalEndPoint {
            get {
                return new IPEndPoint[] {
                    _node1.LocalEndPoint,
                    _node2.LocalEndPoint,
                    _node3.LocalEndPoint,
                    _node4.LocalEndPoint
                };
            }
        }

        public string Guid {
            get {
                return _node1.Guid;
            }
        }

        public override string ToString()
        {
            return Guid;
        }

        #endregion

        public IWebProxy Proxy {
            get {
                return _handler.Proxy;
            }
            set {
                _handler.Proxy = value ?? throw new NullReferenceException();
            }
        }

        public event Action SetToken;
        public event Action<string> SetSite;

    }

    #endregion
}
